//
// Created by martin on 1/27/22.
//

#include "Socket.h"
#include "muduo/base/Types.h"
#include "muduo/base/Logging.h"
#include "muduo/net/SocketsOps.h"
#include "muduo/net/InetAddress.h"

#include <netinet/tcp.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>

#if 0
// from TCP/IP implement of Linux kernel
// https://blog.csdn.net/dyingfair/article/details/95855952
struct tcp_info {
	__u8	tcpi_state;		   //tcp state: TCP_SYN_SENT,TCP_SYN_RECV,TCP_FIN_WAIT1,TCP_CLOSE etc
	__u8	tcpi_ca_state;     //congestion state：
	__u8	tcpi_retransmits;  //重传数，表示当前待重传的包数，这个值在重传完毕后清零
	__u8	tcpi_probes;		///* 持续定时器或保活定时器发送且未确认的段数*/
	__u8	tcpi_backoff;		//用来计算持续定时器的下一个设计值的指数退避算法指数，在传送超时是会递增。
	__u8	tcpi_options;		//tcp头部选项是否包含：扩展因子、时间戳、MSS等内容
	__u8	tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; //扩展因子数值
	__u8	tcpi_delivery_rate_app_limited : 1;  //限速标志

	__u32	tcpi_rto;		//重传超时时间，这个和RTT有关系，RTT越大，rto越大
	__u32	tcpi_ato;		//用来延时确认的估值，单位为微秒.
							//在收到TCP报文时，会根据本次与上次接收的时间间隔来调整改制，在设置延迟确认定时器也会根据
							//条件修改该值
	__u32	tcpi_snd_mss;	// 本端的MSS
	__u32	tcpi_rcv_mss;	// 对端的MSS

	__u32	tcpi_unacked;	//未确认的数据段数
	__u32	tcpi_sacked;    //2个含义：server端在listen阶段，可以接收连接的数量；收到的SACK报文数量
	__u32	tcpi_lost;		//本端在发送出去被丢失的报文数。重传完成后清零
	__u32	tcpi_retrans;   /* 重传且未确认的数据段数 */
	__u32	tcpi_fackets;

	/* Times. */
	__u32	tcpi_last_data_sent;	//当前时间-最近一个包的发送时间，单位是毫秒
	__u32	tcpi_last_ack_sent;     /* 未使用*/
	__u32	tcpi_last_data_recv;	//当前时间-最近接收数据包的时间，单位是毫秒
	__u32	tcpi_last_ack_recv;     //当前时间-最近接收ack的时间，单位是毫秒

	/* Metrics. */
	__u32	tcpi_pmtu;			/* 最后一次更新的路径MTU */
	__u32	tcpi_rcv_ssthresh;   //当前接收窗口的大小
	__u32	tcpi_rtt;			//smoothed round trip time,微妙
	__u32	tcpi_rttvar;		//描述RTT的平均偏差，该值越大，说明RTT抖动越大
	__u32	tcpi_snd_ssthresh;  //拥塞控制慢开始阈值
	__u32	tcpi_snd_cwnd;		//拥塞控制窗口大小
	__u32	tcpi_advmss;		//本端的MSS上限
	__u32	tcpi_reordering;	/* 没有丢包时，可以重新排序的数据段数 */

	__u32	tcpi_rcv_rtt;		// 作为接收端，测出的RTT值，单位为微秒. 这个值不是对方计算并传送过来的rtt，而是作为接收端，在没发送数据的情况下
								// 通过接收发送端发送的数据的情况计算得到的rtt值。在数据发送方，如果不接受数据，这个值一般情况下为0。
	__u32	tcpi_rcv_space;		/* 当前接收缓存的大小 */

	__u32	tcpi_total_retrans;  //统计总重传的包数，持续增长。

	__u64	tcpi_pacing_rate;		//发送速率
	__u64	tcpi_max_pacing_rate;	//最大发送速率，默认是unlimited，可以通过SO_MAX_PACING_RATE来设置
	__u64	tcpi_bytes_acked;    /* RFC4898 tcpEStatsAppHCThruOctetsAcked */
	__u64	tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */
	__u32	tcpi_segs_out;	     /* RFC4898 tcpEStatsPerfSegsOut */
	__u32	tcpi_segs_in;	     /* RFC4898 tcpEStatsPerfSegsIn */

	__u32	tcpi_notsent_bytes;
	__u32	tcpi_min_rtt;
	__u32	tcpi_data_segs_in;	/* RFC4898 tcpEStatsDataSegsIn */
	__u32	tcpi_data_segs_out;	/* RFC4898 tcpEStatsDataSegsOut */

	__u64   tcpi_delivery_rate;

	__u64	tcpi_busy_time;      /* Time (usec) busy sending data */
	__u64	tcpi_rwnd_limited;   /* Time (usec) limited by receive window */
	__u64	tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */
};
#endif

using namespace muduo;
using namespace muduo::net;

Socket::~Socket()
{
	sockets::close(sockfd_);
}

/**
 * 获取TcpInfo(Tcp信息), 存放到tcp_info结构对象tcpi中.
 * @details 调用getsockopt获取TcpInfo, 对应opt name为TCP_INFO.
 * @param tcpi [in] 指向tcp_info结构, 用来存放TcpInfo
 * @return 获取TcpInfo结果
 * - true 获取成功
 * - false 获取失败
 */
bool Socket::getTcpInfo(struct tcp_info* tcpi) const
{
	socklen_t len = sizeof(*tcpi);
	memZero(tcpi, len);
	return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpi, &len) == 0;
}

/**
 * 将从getTcpInfo得到的TcpInfo转换为字符串, 存放到长度为len的buf中.
 * @param buf 存放TcpInfo字符串的缓存
 * @param len 缓存buf的长度, 单位是字节数
 * @return 调用结果
 * - true 返回存放到buf的TcpInfo有效
 * - false 调用getsockopt()出错
 * @see
 * https://blog.csdn.net/zhangskd/article/details/8561950
 */
bool Socket::getTcpInfoString(char* buf, int len) const
{
	struct tcp_info tcpi;
	bool ok = getTcpInfo(&tcpi);
	if (ok)
	{
		snprintf(buf, static_cast<size_t>(len), "unrecovered=%u "
			"rto=%u ato=%u snd_mss=%u rcv_mss=%u "
			"lost=%u retrans=%u rtt=%u rttvar=%u "
			"sshthresh=%u cwnd=%u total_retrans=%u",
			tcpi.tcpi_retransmits,  // 重传数, 当前待重传的包数, 重传完成后清零 //　Number of unrecovered [RTO] timeouts
			tcpi.tcpi_rto,          // 重传超时时间(usec)　// Retransmit timeout in usec
			tcpi.tcpi_ato,          // 延时确认的估值(usec) // Predicted tick of soft clock in usec
			tcpi.tcpi_snd_mss,      // 发送端MSS(最大报文段长度)
			tcpi.tcpi_rcv_mss,      // 接收端MSS(最大报文段长度)
			tcpi.tcpi_lost,         // 丢失且未恢复的数据段数 // Lost packets
			tcpi.tcpi_retrans,      // 重传且未确认的数据段数 // Retransmitted packets out
			tcpi.tcpi_rtt,          // 平滑的RTT(usec)　//　Smoothed round trip time in usec
			tcpi.tcpi_rttvar,       // 1/4 mdev (usec) // Medium deviation
			tcpi.tcpi_snd_ssthresh, // 慢启动阈值
			tcpi.tcpi_snd_cwnd,     // 拥塞窗口
			tcpi.tcpi_total_retrans // 本连接的总重传个数  // Total retransmits for entire connection
		);
	}
	return ok;
}

void Socket::bindAddress(const InetAddress& addr)
{
	sockets::bindOrDie(sockfd_, addr.getSockAddr());
}

// Add by Martin Zhang(zhangming_233@163.com)
int Socket::bindAddressNoDie(const InetAddress& addr)
{
	return sockets::bindNoDie(sockfd_, addr.getSockAddr());
}

void Socket::listen()
{
	sockets::listenOrDie(sockfd_);
}

// Add by Martin Zhang(zhangming_233@163.com)
int Socket::listenNoDie()
{
	return sockets::listenNoDie(sockfd_);
}

int Socket::accept(InetAddress* peeraddr)
{
	struct sockaddr_in6 addr;
	memZero(&addr, sizeof(addr));
	int connfd = sockets::accept(sockfd_, &addr);
	if (connfd >= 0)
	{
		peeraddr->setSockAddrInet6(addr);
	}
	return connfd;
}

void Socket::shutdownWrite()
{
	sockets::shutdownWrite(sockfd_);
}

void Socket::setTcpNoDelay(bool on)
{
	int optval = on ? 1 : 0;
	::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, static_cast<socklen_t>(sizeof(optval)));
	// FIXME CHECK
}

void Socket::setReuseAddr(bool on)
{
	int optval = on ? 1 : 0;
	::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &optval, static_cast<socklen_t>(sizeof(optval)));
	// FIXME CHECK
}

void Socket::setReusePort(bool on)
{
#ifdef SO_REUSEPORT
	int optval = on ? 1 : 0;
	int ret = ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, static_cast<socklen_t>(sizeof(optval)));
	if (ret < 0 && on)
	{
		LOG_SYSERR << "SO_REUSEPORT failed.";
	}
#else
	if (on)
	{
		LOG_ERROR << "SO_REUSEPORT is not supported.";
	}
#endif
}

void Socket::setKeepAlive(bool on)
{
	int optval = on ? 1 : 0;
	::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, static_cast<socklen_t>(sizeof(optval)));
	// FIXME CHECK
}
